#pragma once

#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<unistd.h>
#include<cstring>
#include<string>
#include"log.hpp"
#include"err.hpp"

class Sock//封装所有关于套接字的函数
{
public:
    const static int gbacklog = 5;

    static int Socket()//创建套接字
    {
        int sock = socket(AF_INET, SOCK_STREAM, 0); // 因为我们使用的是udp协议！所以是SOCK_STREAM——字节流！
        if (sock == -1)
        {
            LogMessage(FATAL, "creat socket fail! ");
            exit(SOCKET_ERR);
        }

        LogMessage(NORMAL, "creat socket sucess! "); // 这是一个正常的常规日志！

        //我们要防止服务器挂掉从而处于timewait的状态！而不能立刻重启
        //设置地址复用
        int opt = 1;
        setsockopt(sock,SOL_SOCKET,SO_REUSEADDR | SO_REUSEPORT,&opt,sizeof(opt));
        return sock;
    }

    static void Bind(int sock,int port)//绑定套接字
    {
        struct sockaddr_in peer;
        bzero(&peer, sizeof(peer));
        peer.sin_family = AF_INET;         // PF_INET也是可以的！两个是同一个的值不同的宏名字
        peer.sin_addr.s_addr = INADDR_ANY; // 服务器ip不需要一个固定的地址！
        peer.sin_port = htons(port);
        int n = bind(sock, (struct sockaddr *)&peer, sizeof(peer));
        if (n == -1)
        {
            LogMessage(FATAL, "bind socket fail! "); // 这是一个FATAL的日志
            exit(BIND_ERR);
        }

        LogMessage(NORMAL, "bind socket sucess! "); // 这是一个正常的常规日志！
    }


    static void Listen(int sock)//将套接字设置为监听状态
    {
        if (listen(sock, gbacklog) < 0)
        {
            LogMessage(FATAL, "listen socket fail! "); // 这是一个FATAL的日志
            exit(LISTEN_ERR);
        }
        LogMessage(NORMAL, "listen socket sucess! "); // 这是一个正常的常规日志！
    }


    static int Accept(int listensock,std::string *clientip,uint16_t* clientport)//获取连接
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int sock = accept(listensock, (struct sockaddr *)&peer, &len);

        if (sock < 0)
        {
            LogMessage(ERROR, "accept fail! next"); // 这是一个错误！但是不是一个很严重的错误！
            // 不影响进程的接下来的正常运行！
            return ACCEPT_ERR;
        }
        LogMessage(NORMAL, "accept a link success!"); // 这是一个错误！但是不是一个很严重的错误！
        *clientip = inet_ntoa(peer.sin_addr);         // 获取ip
        *clientport = ntohs(peer.sin_port);           // 获取端口号



        return sock;
    }

};
